import { PrismaClient } from "@prisma/client";
import { createHmac } from "crypto";
import { BinaryRespond, createSwRt, Get, isVoid, limitRate, MaybePromise, Put, useURL } from "freesia";
import { nanoid } from "nanoid";
import { serverKey } from "./CONSTANTS";

const db = new PrismaClient();

function signId(id: string) {
    return createHmac("sha256", serverKey).update(id).digest("base64url");
}

function hasValidId() {
    const id = useURL("query").get("id");
    const sign = useURL("query").get("sign");
    if (isVoid(id) || isVoid(sign)) return false
    if (signId(id) === sign) return id
    return false
}

export const { switcher } = createSwRt<MaybePromise<BinaryRespond>>()
    .route("/api/visited/nanoid", Get(limitRate((): BinaryRespond => {
        const id = nanoid();
        const sign = signId(id);
        return [200, JSON.stringify({ id, sign }), { "Content-Type": "application/json" }]
    }, { max: 100, windowSize: 10 * 1000, fallback: [400, "To many requests"] })))
    .route("/api/visited/", Get(async () => {
        try {
            const data = await db.visited.findMany({
                select: {
                    path: true, nanoIds: true
                }
            })
            const result = data.map(log => ({ path: log.path, count: log.nanoIds.length }))
            return [200, JSON.stringify(result), { "Content-Type": "application/json" }]
        } catch {
            return [500, null]
        }
    }))
    .route("/api/visited/:[path]", Put(async ({ path }) => {
        const id = hasValidId();
        if (id === false) return [403, "Invalid or not give an ID"]
        try {
            const existedRec = await db.visited.findUnique({
                where: { path: decodeURI(path) }, select: { nanoIds: true }
            })
            if (existedRec === null) {
                await db.visited.create({
                    data: { path: decodeURI(path), nanoIds: [id] }
                })
                return [200, "Ok"]
            }
            if (existedRec.nanoIds.includes(id)) {
                return [200, "Ok"]
            }
            await db.visited.update({
                where: { path: decodeURI(path) },
                data: {
                    nanoIds: {
                        push: id
                    }
                }
            })
            return [200, "Ok"]
        } catch (err) {
            console.log(err)
            return [500, "Something happend when writing DB"]
        }
    }))
    .fallback(() => [404, null])
